/*
 * txtquery io
 * Teodor Sigaev <teodor@stack.net>
 * contrib/ltree/ltxtquery_io.c
 */
#include "postgres.h"

#include <ctype.h>

#include "crc32.h"
#include "libpq/pqformat.h"
#include "ltree.h"
#include "miscadmin.h"


/* 解析器的状态 */
#define WAITOPERAND 1
#define INOPERAND 2
#define WAITOPERATOR	3

/*
 * 查询树的节点，也用于
 * 在解析器中存储波兰表示法
 */
typedef struct NODE
{
	int32		type;
	int32		val;
	int16		distance;
	int16		length;
	uint16		flag;
	struct NODE *next;
} NODE;

typedef struct
{
	char	   *buf;
	int32		state;
	int32		count;
	/* 列表中的逆波兰表示法（用于临时使用） */
	NODE	   *str;
	/* 字符串中的数字 */
	int32		num;

	/* 用户友好的操作数 */
	int32		lenop;
	int32		sumlen;
	char	   *op;
	char	   *curop;
} QPRS_STATE;

/*
 * 从查询字符串中获取标记
 */
static int32 fc_gettoken_query(QPRS_STATE *fc_state, int32 *fc_val, int32 *fc_lenval, char **fc_strval, uint16 *fc_flag)
{
	int			fc_charlen;

	for (;;)
	{
		fc_charlen = pg_mblen(fc_state->buf);

		switch (fc_state->state)
		{
			case WAITOPERAND:
				if (fc_charlen == 1 && t_iseq(fc_state->buf, '!'))
				{
					(fc_state->buf)++;
					*fc_val = (int32) '!';
					return OPR;
				}
				else if (fc_charlen == 1 && t_iseq(fc_state->buf, '('))
				{
					fc_state->count++;
					(fc_state->buf)++;
					return OPEN;
				}
				else if (ISALNUM(fc_state->buf))
				{
					fc_state->state = INOPERAND;
					*fc_strval = fc_state->buf;
					*fc_lenval = fc_charlen;
					*fc_flag = 0;
				}
				else if (!t_isspace(fc_state->buf))
					ereport(ERROR,
							(errcode(ERRCODE_SYNTAX_ERROR),
							 errmsg("operand syntax error")));
				break;
			case INOPERAND:
				if (ISALNUM(fc_state->buf))
				{
					if (*fc_flag)
						ereport(ERROR,
								(errcode(ERRCODE_SYNTAX_ERROR),
								 errmsg("modifiers syntax error")));
					*fc_lenval += fc_charlen;
				}
				else if (fc_charlen == 1 && t_iseq(fc_state->buf, '%'))
					*fc_flag |= LVAR_SUBLEXEME;
				else if (fc_charlen == 1 && t_iseq(fc_state->buf, '@'))
					*fc_flag |= LVAR_INCASE;
				else if (fc_charlen == 1 && t_iseq(fc_state->buf, '*'))
					*fc_flag |= LVAR_ANYEND;
				else
				{
					fc_state->state = WAITOPERATOR;
					return VAL;
				}
				break;
			case WAITOPERATOR:
				if (fc_charlen == 1 && (t_iseq(fc_state->buf, '&') || t_iseq(fc_state->buf, '|')))
				{
					fc_state->state = WAITOPERAND;
					*fc_val = (int32) *(fc_state->buf);
					(fc_state->buf)++;
					return OPR;
				}
				else if (fc_charlen == 1 && t_iseq(fc_state->buf, ')'))
				{
					(fc_state->buf)++;
					fc_state->count--;
					return (fc_state->count < 0) ? ERR : CLOSE;
				}
				else if (*(fc_state->buf) == '\0')
					return (fc_state->count) ? ERR : END;
				else if (fc_charlen == 1 && !t_iseq(fc_state->buf, ' '))
					return ERR;
				break;
			default:
				return ERR;
				break;
		}

		fc_state->buf += fc_charlen;
	}
}

/*
 * 在波兰表示法的逆视图中推入新的元素
 */
static void fc_pushquery(QPRS_STATE *fc_state, int32 fc_type, int32 fc_val, int32 fc_distance, int32 fc_lenval, uint16 fc_flag)
{
	NODE	   *fc_tmp = (NODE *) palloc(sizeof(NODE));

	fc_tmp->type = fc_type;
	fc_tmp->val = fc_val;
	fc_tmp->flag = fc_flag;
	if (fc_distance > 0xffff)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("value is too big")));
	if (fc_lenval > 0xff)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("operand is too long")));
	fc_tmp->distance = fc_distance;
	fc_tmp->length = fc_lenval;
	fc_tmp->next = fc_state->str;
	fc_state->str = fc_tmp;
	fc_state->num++;
}

/*
 * 此函数用于查询文本解析
 */
static void fc_pushval_asis(QPRS_STATE *fc_state, int fc_type, char *fc_strval, int fc_lenval, uint16 fc_flag)
{
	if (fc_lenval > 0xffff)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("word is too long")));

	fc_pushquery(fc_state, fc_type, ltree_crc32_sz(fc_strval, fc_lenval),
			  fc_state->curop - fc_state->op, fc_lenval, fc_flag);

	while (fc_state->curop - fc_state->op + fc_lenval + 1 >= fc_state->lenop)
	{
		int32		fc_tmp = fc_state->curop - fc_state->op;

		fc_state->lenop *= 2;
		fc_state->op = (char *) repalloc((void *) fc_state->op, fc_state->lenop);
		fc_state->curop = fc_state->op + fc_tmp;
	}
	memcpy((void *) fc_state->curop, (void *) fc_strval, fc_lenval);
	fc_state->curop += fc_lenval;
	*(fc_state->curop) = '\0';
	fc_state->curop++;
	fc_state->sumlen += fc_lenval + 1;
}

#define STACKDEPTH		32
/*
 * 构建查询的波兰表示法
 */
static int32 fc_makepol(QPRS_STATE *fc_state)
{
	int32		fc_val = 0,
				fc_type;
	int32		fc_lenval = 0;
	char	   *fc_strval = NULL;
	int32		fc_stack[STACKDEPTH];
	int32		fc_lenstack = 0;
	uint16		fc_flag = 0;

	/* 由于这个函数递归，可能会导致栈溢出 */
	check_stack_depth();

	while ((fc_type = fc_gettoken_query(fc_state, &fc_val, &fc_lenval, &fc_strval, &fc_flag)) != END)
	{
		switch (fc_type)
		{
			case VAL:
				fc_pushval_asis(fc_state, VAL, fc_strval, fc_lenval, fc_flag);
				while (fc_lenstack && (fc_stack[fc_lenstack - 1] == (int32) '&' ||
									fc_stack[fc_lenstack - 1] == (int32) '!'))
				{
					fc_lenstack--;
					fc_pushquery(fc_state, OPR, fc_stack[fc_lenstack], 0, 0, 0);
				}
				break;
			case OPR:
				if (fc_lenstack && fc_val == (int32) '|')
					fc_pushquery(fc_state, OPR, fc_val, 0, 0, 0);
				else
				{
					if (fc_lenstack == STACKDEPTH)
						/* 内部错误 */
						elog(ERROR, "stack too short");
					fc_stack[fc_lenstack] = fc_val;
					fc_lenstack++;
				}
				break;
			case OPEN:
				if (fc_makepol(fc_state) == ERR)
					return ERR;
				while (fc_lenstack && (fc_stack[fc_lenstack - 1] == (int32) '&' ||
									fc_stack[fc_lenstack - 1] == (int32) '!'))
				{
					fc_lenstack--;
					fc_pushquery(fc_state, OPR, fc_stack[fc_lenstack], 0, 0, 0);
				}
				break;
			case CLOSE:
				while (fc_lenstack)
				{
					fc_lenstack--;
					fc_pushquery(fc_state, OPR, fc_stack[fc_lenstack], 0, 0, 0);
				};
				return END;
				break;
			case ERR:
			default:
				ereport(ERROR,
						(errcode(ERRCODE_SYNTAX_ERROR),
						 errmsg("syntax error")));

				return ERR;
		}
	}
	while (fc_lenstack)
	{
		fc_lenstack--;
		fc_pushquery(fc_state, OPR, fc_stack[fc_lenstack], 0, 0, 0);
	};
	return END;
}

static void fc_findoprnd(ITEM *fc_ptr, int32 *fc_pos)
{
	/* 由于这个函数递归，它可能导致栈溢出。 */
	check_stack_depth();

	if (fc_ptr[*fc_pos].type == VAL || fc_ptr[*fc_pos].type == VALTRUE)
	{
		fc_ptr[*fc_pos].left = 0;
		(*fc_pos)++;
	}
	else if (fc_ptr[*fc_pos].val == (int32) '!')
	{
		fc_ptr[*fc_pos].left = 1;
		(*fc_pos)++;
		fc_findoprnd(fc_ptr, fc_pos);
	}
	else
	{
		ITEM	   *fc_curitem = &fc_ptr[*fc_pos];
		int32		fc_tmp = *fc_pos;

		(*fc_pos)++;
		fc_findoprnd(fc_ptr, fc_pos);
		fc_curitem->left = *fc_pos - fc_tmp;
		fc_findoprnd(fc_ptr, fc_pos);
	}
}


/*
 * 输入
 */
static ltxtquery *
fc_queryin(char *fc_buf)
{
	QPRS_STATE	fc_state;
	int32		fc_i;
	ltxtquery  *fc_query;
	int32		fc_commonlen;
	ITEM	   *fc_ptr;
	NODE	   *fc_tmp;
	int32		fc_pos = 0;

#ifdef BS_DEBUG
	char		fc_pbuf[16384],
			   *fc_cur;
#endif

	/* 初始化状态 */
	fc_state.buf = fc_buf;
	fc_state.state = WAITOPERAND;
	fc_state.count = 0;
	fc_state.num = 0;
	fc_state.str = NULL;

	/* 初始化操作数列表 */
	fc_state.sumlen = 0;
	fc_state.lenop = 64;
	fc_state.curop = fc_state.op = (char *) palloc(fc_state.lenop);
	*(fc_state.curop) = '\0';

	/* 解析查询并生成波兰表示法（后缀，但顺序反向） */
	fc_makepol(&fc_state);
	if (!fc_state.num)
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("syntax error"),
				 errdetail("Empty query.")));

	if (LTXTQUERY_TOO_BIG(fc_state.num, fc_state.sumlen))
		ereport(ERROR,
				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
				 errmsg("ltxtquery is too large")));
	fc_commonlen = COMPUTESIZE(fc_state.num, fc_state.sumlen);

	fc_query = (ltxtquery *) palloc0(fc_commonlen);
	SET_VARSIZE(fc_query, fc_commonlen);
	fc_query->size = fc_state.num;
	fc_ptr = GETQUERY(fc_query);

	/* 设置波兰表示法中的项 */
	for (fc_i = 0; fc_i < fc_state.num; fc_i++)
	{
		fc_ptr[fc_i].type = fc_state.str->type;
		fc_ptr[fc_i].val = fc_state.str->val;
		fc_ptr[fc_i].distance = fc_state.str->distance;
		fc_ptr[fc_i].length = fc_state.str->length;
		fc_ptr[fc_i].flag = fc_state.str->flag;
		fc_tmp = fc_state.str->next;
		pfree(fc_state.str);
		fc_state.str = fc_tmp;
	}

	/* 设置用户友好的操作数视图 */
	memcpy((void *) GETOPERAND(fc_query), (void *) fc_state.op, fc_state.sumlen);
	pfree(fc_state.op);

	/* 为每个操作符设置左操作数的位置 */
	fc_pos = 0;
	fc_findoprnd(fc_ptr, &fc_pos);

	return fc_query;
}

/*
 * 不带形态学的
 */
PG_FUNCTION_INFO_V1(ltxtq_in);
Datum ltxtq_in(PG_FUNCTION_ARGS)
{
	PG_RETURN_POINTER(fc_queryin((char *) PG_GETARG_POINTER(0)));
}

/*
 * ltxtquery 类型接收函数
 *
 * 该类型以二进制模式作为文本发送，所以这几乎与
 * 输入函数相同，但它前面加上了版本号，以便我们
 * 将来如有必要可以更改发送的二进制格式。目前，
 * 仅支持版本 1。
 */
PG_FUNCTION_INFO_V1(ltxtq_recv);
Datum ltxtq_recv(PG_FUNCTION_ARGS)
{
	StringInfo	fc_buf = (StringInfo) PG_GETARG_POINTER(0);
	int			fc_version = pq_getmsgint(fc_buf, 1);
	char	   *fc_str;
	int			fc_nbytes;
	ltxtquery  *fc_res;

	if (fc_version != 1)
		elog(ERROR, "unsupported ltxtquery version number %d", fc_version);

	fc_str = pq_getmsgtext(fc_buf, fc_buf->len - fc_buf->cursor, &fc_nbytes);
	fc_res = fc_queryin(fc_str);
	pfree(fc_str);

	PG_RETURN_POINTER(fc_res);
}

/*
 * 输出函数
 */
typedef struct
{
	ITEM	   *curpol;
	char	   *buf;
	char	   *cur;
	char	   *op;
	int32		buflen;
} INFIX;

#define RESIZEBUF(inf,addsize) \
while( ( (inf)->cur - (inf)->buf ) + (addsize) + 1 >= (inf)->buflen ) \
{ \
	int32 fc_len = (inf)->cur - (inf)->buf; \
	(inf)->buflen *= 2; \
	(inf)->buf = (char*) repalloc( (void*)(inf)->buf, (inf)->buflen ); \
	(inf)->cur = (inf)->buf + fc_len; \
}

/*
 * 在树上递归遍历并以
 * 中缀（人类可读）视图打印
 */
static void fc_infix(INFIX *fc_in, bool fc_first)
{
	/* 由于这个函数递归，它可能导致栈溢出。 */
	check_stack_depth();

	if (fc_in->curpol->type == VAL)
	{
		char	   *fc_op = fc_in->op + fc_in->curpol->distance;

		RESIZEBUF(fc_in, fc_in->curpol->length * 2 + 5);
		while (*fc_op)
		{
			*(fc_in->cur) = *fc_op;
			fc_op++;
			fc_in->cur++;
		}
		if (fc_in->curpol->flag & LVAR_SUBLEXEME)
		{
			*(fc_in->cur) = '%';
			fc_in->cur++;
		}
		if (fc_in->curpol->flag & LVAR_INCASE)
		{
			*(fc_in->cur) = '@';
			fc_in->cur++;
		}
		if (fc_in->curpol->flag & LVAR_ANYEND)
		{
			*(fc_in->cur) = '*';
			fc_in->cur++;
		}
		*(fc_in->cur) = '\0';
		fc_in->curpol++;
	}
	else if (fc_in->curpol->val == (int32) '!')
	{
		bool		fc_isopr = false;

		RESIZEBUF(fc_in, 1);
		*(fc_in->cur) = '!';
		fc_in->cur++;
		*(fc_in->cur) = '\0';
		fc_in->curpol++;
		if (fc_in->curpol->type == OPR)
		{
			fc_isopr = true;
			RESIZEBUF(fc_in, 2);
			sprintf(fc_in->cur, "( ");
			fc_in->cur = strchr(fc_in->cur, '\0');
		}
		fc_infix(fc_in, fc_isopr);
		if (fc_isopr)
		{
			RESIZEBUF(fc_in, 2);
			sprintf(fc_in->cur, " )");
			fc_in->cur = strchr(fc_in->cur, '\0');
		}
	}
	else
	{
		int32		fc_op = fc_in->curpol->val;
		INFIX		fc_nrm;

		fc_in->curpol++;
		if (fc_op == (int32) '|' && !fc_first)
		{
			RESIZEBUF(fc_in, 2);
			sprintf(fc_in->cur, "( ");
			fc_in->cur = strchr(fc_in->cur, '\0');
		}

		fc_nrm.curpol = fc_in->curpol;
		fc_nrm.op = fc_in->op;
		fc_nrm.buflen = 16;
		fc_nrm.cur = fc_nrm.buf = (char *) palloc(sizeof(char) * fc_nrm.buflen);

		/* 获取右操作数 */
		fc_infix(&fc_nrm, false);

		/* 获取并打印左操作数 */
		fc_in->curpol = fc_nrm.curpol;
		fc_infix(fc_in, false);

		/* 打印操作符和右操作数 */
		RESIZEBUF(fc_in, 3 + (fc_nrm.cur - fc_nrm.buf));
		sprintf(fc_in->cur, " %c %s", fc_op, fc_nrm.buf);
		fc_in->cur = strchr(fc_in->cur, '\0');
		pfree(fc_nrm.buf);

		if (fc_op == (int32) '|' && !fc_first)
		{
			RESIZEBUF(fc_in, 2);
			sprintf(fc_in->cur, " )");
			fc_in->cur = strchr(fc_in->cur, '\0');
		}
	}
}

PG_FUNCTION_INFO_V1(ltxtq_out);
Datum ltxtq_out(PG_FUNCTION_ARGS)
{
	ltxtquery  *fc_query = PG_GETARG_LTXTQUERY_P(0);
	INFIX		fc_nrm;

	if (fc_query->size == 0)
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("syntax error"),
				 errdetail("Empty query.")));

	fc_nrm.curpol = GETQUERY(fc_query);
	fc_nrm.buflen = 32;
	fc_nrm.cur = fc_nrm.buf = (char *) palloc(sizeof(char) * fc_nrm.buflen);
	*(fc_nrm.cur) = '\0';
	fc_nrm.op = GETOPERAND(fc_query);
	fc_infix(&fc_nrm, true);

	PG_RETURN_POINTER(fc_nrm.buf);
}

/*
 * ltxtquery 类型发送函数
 *
 * 该类型以二进制模式作为文本发送，所以这几乎与
 * 输出函数相同，但它前面加上了版本号，以便我们
 * 将来如有必要可以更改发送的二进制格式。目前，
 * 仅支持版本 1。
 */
PG_FUNCTION_INFO_V1(ltxtq_send);
Datum ltxtq_send(PG_FUNCTION_ARGS)
{
	ltxtquery  *fc_query = PG_GETARG_LTXTQUERY_P(0);
	StringInfoData fc_buf;
	int			fc_version = 1;
	INFIX		fc_nrm;

	if (fc_query->size == 0)
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("syntax error"),
				 errdetail("Empty query.")));

	fc_nrm.curpol = GETQUERY(fc_query);
	fc_nrm.buflen = 32;
	fc_nrm.cur = fc_nrm.buf = (char *) palloc(sizeof(char) * fc_nrm.buflen);
	*(fc_nrm.cur) = '\0';
	fc_nrm.op = GETOPERAND(fc_query);
	fc_infix(&fc_nrm, true);

	pq_begintypsend(&fc_buf);
	pq_sendint8(&fc_buf, fc_version);
	pq_sendtext(&fc_buf, fc_nrm.buf, strlen(fc_nrm.buf));
	pfree(fc_nrm.buf);

	PG_RETURN_BYTEA_P(pq_endtypsend(&fc_buf));
}
